import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;
import java.net.*;
import java.io.*;
import java.util.*;
import java.lang.Long;

public class TaskExecutionChatServerRMI implements ServerRMI { 
  private HashMap<String, ClientRMI> group;
  private HashMap<String, Long> heartbeats;

  public TaskExecutionChatServerRMI(){
    group = new HashMap<String, ClientRMI>();
    heartbeats = new HashMap<String, Long>();

    //start watchdog to clean up dead clients
    ClientWatchdog watchdog = new ClientWatchdog();
    watchdog.start();
  }

  private class ClientWatchdog extends Thread {
    public void run() {
      System.out.println("Client watchdog started");
      // Cleanup old clients
      while (true) {
        try{
          synchronized (group) {
            for(String key : group.keySet()){
              ClientRMI client = group.get(key);
              long currentTime = System.currentTimeMillis();
              long lastHeartbeat = heartbeats.get(key).longValue();
              if (currentTime - lastHeartbeat > 60*1000) {
                System.out.println("Removing idle client " + key);
                group.remove(key); 
                heartbeats.remove(key);
                break;
              }
            }
          }
          Thread.sleep(5000);
        } catch(InterruptedException d) {
          System.err.println("Error: " + d.getMessage());
        }
      }
    }
  }

  public void heartbeat(ClientRMI client) throws RemoteException {
    synchronized (group) {
      if(group.containsKey(client.getName())) {
        System.out.println("Recieved heartbeat from " + client.getName());
        heartbeats.put(client.getName(), new Long(System.currentTimeMillis()));
      } else {
        System.err.println("Recieved heartbeat from unregistered client " +
            client.getName());
      }
    }
  }

  public boolean registerClient(ClientRMI client) throws RemoteException{
    synchronized (group) {
      if(group.containsKey(client.getName())){
        return false;
      }else{
        heartbeats.put(client.getName(), new Long(System.currentTimeMillis()));
        group.put(client.getName(), client);
        System.out.println("Registered client " + client.getName());
        return true;
      }
    }
  }

  public boolean unregisterClient(ClientRMI client) throws RemoteException{
    synchronized (group) {
      if(group.containsKey(client.getName())){
        System.out.println("Removing client " + client.getName());
        group.remove(client.getName());
        heartbeats.remove(client.getName());
        return true;
      }else{
        return false;
      }
    }
  }

  public HashMap<String, ClientRMI> getGroup() throws RemoteException{
    System.out.println("Sending group list");
    return group;
  }

  public static void main(String[] args) {
    //install the security manager
    if(System.getSecurityManager() == null) {
      System.setSecurityManager(new SecurityManager());
    }
    try {
      int serverPort;
      if(args.length == 1) {
        serverPort = Integer.parseInt(args[0]);
        String name = "ServerRMI";
        ServerRMI server = new TaskExecutionChatServerRMI();
        ServerRMI stub = 
          (ServerRMI)UnicastRemoteObject.exportObject(server,serverPort);
        Registry registry = LocateRegistry.getRegistry();
        registry.rebind(name, stub);
      }else{
        System.err.println("Usage: TaskExecutionChatServerRMI <serverPort>");
        System.exit(1);
      }
    }catch(Exception e){
      System.err.println(e.getMessage());
      System.exit(1);
    }
  }
}
